﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using sscore;
using sscore.Utils;
using DM4;

namespace DMN_MailSender
{
    internal class Worker : IDisposable
    {
        public readonly Dictionary<DateTime, Task> Tasks;
        private CancellationTokenSource _cts;

        private readonly LogHandler _writeLog;
        private readonly WorkerResultHandler _workerResultHandler;
        private readonly WorkerResult _workerResult;
        private readonly string _connectionString;


        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
                if (_cts != null)
                {
                    _cts.Dispose();
                    _cts = null;
                }
        }
        ~Worker()
        {
            Dispose(false);
        }
        public Worker(LogHandler writeLog, WorkerResultHandler workerResultHandler, string connectionString)
        {
            _writeLog = writeLog;
            _workerResultHandler = workerResultHandler;
            _cts = new CancellationTokenSource();
            _connectionString = connectionString;

            _workerResult = new WorkerResult();
            Tasks = new Dictionary<DateTime, Task>();
        }

        public void Start(MailSenderParameters mailParam)
        {
            Start(new List<MailSenderParameters> { mailParam });
        }

        public void Start(List<MailSenderParameters> mailParams)
        {            
            //Создаем отдельный таск для вставки в базу параметров отсылки писем
            //В этом таске, после вставки в базу, вызовем функцию рассылки
            Action<object> action = CreateMailDbParams;
            CreateMailParamsTaskParams cmtp = new CreateMailParamsTaskParams() { Ct = _cts.Token, Mswp = mailParams, MailSend = Send };
            Task task = new Task(action, cmtp, _cts.Token);
            task.Start();                  
        }
        private void CreateMailDbParams(object prm)
        {
            CreateMailParamsTaskParams cmtp = (CreateMailParamsTaskParams)prm;
            WorkWithDbData db = new WorkWithDbData(_connectionString, _writeLog);
            List<MailSenderWorkerParameters> mswp = db.CreateMailSenderParameters(cmtp.Mswp);

            //вызываем  функцию рассылки через делегат
            if (cmtp.MailSend != null)
                cmtp.MailSend(mswp);
        }

        public void Send(List<MailSenderWorkerParameters> mailParams)
        {
            if (mailParams != null && mailParams.Count != 0)
            {
                WriteLog(String.Format("Start send new mail, count [{0}]", mailParams.Count), MessageType.Cool);
                foreach (MailSenderWorkerParameters mp in mailParams)
                {
                    if (_cts.IsCancellationRequested) break;

                    TaskParams tprm = new TaskParams { Ct = _cts.Token, Log = _writeLog, MailParams = mp, ConnectionString = _connectionString };
                    tprm.Spamer = new Spamer(OnOneTaskResult, tprm);
                    CreateAndStartTask(tprm.MailParams.StartTime, tprm);
                }
            }
            else
            {
                WriteLog("New mail parameters not found for send", MessageType.Cool);
                SetWorkerResult();
            }
        }

        private void CreateAndStartTask(DateTime taskId, TaskParams tprm)
        {
            SysUtils.Sleeps.MSeconds(20);
            Action<object> action = StartOneTask;

            if (Tasks.ContainsKey(taskId))
                Tasks.Remove(taskId);

            Tasks.Add(taskId, new Task(action, tprm, _cts.Token));
            Tasks[taskId].Start();
        }

        private void StartOneTask(object prm)
        {
            TaskParams tPrm = (TaskParams)prm;
            CancellationToken ct = tPrm.Ct;

            ct.ThrowIfCancellationRequested();

            if (!ct.IsCancellationRequested)
                tPrm.Spamer.DoWork();
        }

        private void OnOneTaskResult(TaskParams tprm)
        {
            _workerResult.CompleteCount++;

            if (tprm.MailParams.Result == Consts.FALSE)
                _workerResult.ErrorCount++;

            if (Tasks.ContainsKey(tprm.MailParams.StartTime))
                Tasks.Remove(tprm.MailParams.StartTime);

            if (Tasks.Count == 0)
                SetWorkerResult();
        }

        public void Stop()
        {
            _cts.Cancel();
            Tasks.Clear();
            WriteLog("Spamer worker is stopped.", MessageType.Error);
        }


        private void SetWorkerResult()
        {
            if (_workerResultHandler != null)
                _workerResultHandler(_workerResult);
        }

        private void WriteLog(string aMessage, MessageType type = MessageType.Info)
        {
            if (_writeLog != null)
                _writeLog(aMessage, type);
        }
    }
}
